using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;

namespace BookBarter
{
    public class TradeRoute
    {
        private static List<TradeRoute> tradeRoutes = new List<TradeRoute>();

        //class variables
        private Guid routeid;
        private CompletedStatus completed;
        private TradeLink firstLink;
        private TradeLink lastLink;
        private int size;

        //class properties
        public Guid ID
        {
            get { return routeid; }
            private set { routeid = value; }
        }
        public CompletedStatus Completed
        {
            get { return completed; }
            set
            {
                completed = value;
                Save();
            }
        }
        public TradeLink FirstLink
        {
            get { return firstLink; }
            private set { firstLink = value; }
        }
        public TradeLink LastLink
        {
            get { return lastLink; }
            private set { lastLink = value; }
        }
        public int Size
        {
            get { return size; }
            private set { size = value; }
        }


        //constructor
        private TradeRoute(Guid routeID, CompletedStatus completed)
        {
            this.ID = routeID;
            this.Completed = completed;
            this.FirstLink = null;
            this.LastLink = null;
            this.size = 0;
        }

        /// <summary>
        /// Creates a new TradeRoute Object
        /// </summary>
        /// <returns>Returns the created object</returns>
        public static TradeRoute CreateNewTradeRoute()
        {
            try
            {
                TradeRoute newRoute = null;
                SqlConnection connection = DatabaseConnectionManager.Current.Connection;
                SqlCommand query = new SqlCommand("EXEC add_trade_route", connection);
                SqlDataReader reader = query.ExecuteReader();
                try
                {
                    if (reader.HasRows)
                    {
                        reader.Read();
                        if (reader.IsDBNull(reader.GetOrdinal("routeid")))
                            throw new MalformedTradeRouteRecord("NULL where data is expected for record ID.");
                        newRoute = TradeRoute.GetTradeRoute(reader.GetGuid(reader.GetOrdinal("routeid")));
                    }
                }
                finally
                {
                    reader.Close();
                }
                if (newRoute == null)
                    throw new NoSuchTradeRoute("No trade route exists for the new route ID.");
                tradeRoutes.Add(newRoute);
                return newRoute;
            }
            catch (DatabaseException e)
            {
                throw new NewTradeRouteException("Unable to create TradeRoute object.  See Inner Exception for more details.", e);
            }
        }

        /// <summary>
        /// gets a TradeRoute with a specific guid
        /// </summary>
        /// <param name="routeid">guid of desired TradeRoute</param>
        /// <returns>Returns desired TradeRoute</returns>
        public static TradeRoute GetTradeRoute(Guid routeid)
        {
            TradeRoute requestedRoute = null;
            SqlConnection connection = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC get_trade_route @routeid", connection);
            query.Parameters.Add("@routeid", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@routeid"].SqlValue = routeid;
            SqlDataReader reader = query.ExecuteReader();
            try
            {
                if (reader.HasRows)
                {
                    reader.Read();
                    if (reader.IsDBNull(reader.GetOrdinal("routeid")))
                        throw new MalformedTradeRouteRecord(String.Format("NULL where data is expected for record with ID: {0}", routeid));
                    Guid newRouteID = reader.GetGuid(reader.GetOrdinal("routeid"));
                    CompletedStatus newCompletedStatus;
                    if (reader.IsDBNull(reader.GetOrdinal("completed")))
                        newCompletedStatus = CompletedStatus.InProgress;
                    else
                        if (reader.GetBoolean(reader.GetOrdinal("completed")))
                            newCompletedStatus = CompletedStatus.Completed;
                        else
                            newCompletedStatus = CompletedStatus.Cancelled;
                    requestedRoute = new TradeRoute(newRouteID, newCompletedStatus);
                }
            }
            finally
            {
                reader.Close();
            }
            if (requestedRoute == null)
                throw new NoSuchTradeRoute(String.Format("No trade route exists for ID: {0}", routeid));
            tradeRoutes.Add(requestedRoute);
            requestedRoute.LoadLinks();
            return requestedRoute;
        }

        // loads the tradelinks stored in the DB into the TradeRoute that calls the function.
        // should be used only when getting a saved trade route from the db.
        private void LoadLinks()
        {
            SqlConnection connection = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC get_trade_links @routeid", connection);
            query.Parameters.Add("@routeid", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@routeid"].SqlValue = this.ID;
            SqlDataReader reader = query.ExecuteReader();
            try
            {
                while (reader.Read())
                    this.AddTradeLink(TradeLink.GetTradeLink(reader.GetGuid(reader.GetOrdinal("linkid")), this));
            }
            finally
            {
                reader.Close();
            }
        }

        /// <summary>
        /// Saves the current TradeRoute to the database
        /// </summary>
        public void Save()
        {
            SqlConnection connection = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC update_trade_route @routeid, @completed", connection);
            query.Parameters.Add("@routeid", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@routeid"].SqlValue = this.ID;

            query.Parameters.Add("@completed", System.Data.SqlDbType.Bit);
            if (this.completed == CompletedStatus.Completed)
                query.Parameters["@completed"].SqlValue = 1;
            else if (this.completed == CompletedStatus.Cancelled)
                query.Parameters["@completed"].SqlValue = 0;
            else
                query.Parameters["@completed"].SqlValue = DBNull.Value;

            query.ExecuteNonQuery();
        }

        // Internally adds the link to the route.  To create a new link, AddTradeLink(BookHave, BookWant) below.
        // There is no other way to create a TradeLink since a link must be part of a route.
        private void AddTradeLink(TradeLink newLink)
        {
            if (this.FirstLink == null)
            {
                this.FirstLink = newLink;
                this.LastLink = newLink;
            }
            else
            {
                this.LastLink.NextTradeLink = newLink;
                this.LastLink = newLink;
                this.LastLink.NextTradeLink = null;
            }
            this.Size++;
        }


        //*****This needs looked into.  Don't believe we need this we just need to add it to the above method
        /// <summary>
        /// Adds A new TradeLink to the TradeRoute
        /// </summary>
        /// <param name="newLink">Link to be added</param>
        public TradeLink AddTradeLink(BookHave have, BookWant want)
        {
            TradeLink newLink = TradeLink.CreateNewTradeLink(this, have, want);
            SqlConnection connection = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC add_link_to_route @routeid, @linkid", connection);
            query.Parameters.Add("@routeid", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@routeid"].SqlValue = this.ID;
            query.Parameters.Add("@linkid", System.Data.SqlDbType.UniqueIdentifier);
            query.Parameters["@linkid"].SqlValue = newLink.ID;
            query.ExecuteNonQuery();
            this.AddTradeLink(newLink);
            return newLink;
        }

		/// <summary>
		/// Determines if a given user is contained within a link
		/// </summary>
		/// <param name="user">User to check</param>
		/// <returns>True if the given user is contained within any link, else false</returns>
        public bool IncludesPerson(Person user)
        {
            TradeLink link = this.FirstLink;
            while (link != null)
                if (link.Have.Person == user)
                    return true;
                else
                    link = link.NextTradeLink;
            return false;
        }

		/// <summary>
		/// Returns a List of all trade routes that are not notified
		/// </summary>
		/// <returns>A List of TradeRoute objects which are marked as not notified</returns>
        public static List<TradeRoute> GetAllTradeRoutesNotNotified()
        {

            List<TradeRoute> routesNotNotified = new List<TradeRoute>();
            SqlConnection cnx = DatabaseConnectionManager.Current.Connection;
            SqlCommand query = new SqlCommand("EXEC get_all_trade_routes_not_notified", cnx);
            SqlDataReader result = query.ExecuteReader();
            try
            {
                while (result.Read())
                {
                    routesNotNotified.Add(TradeRoute.GetTradeRoute(result.GetGuid(result.GetOrdinal("routeid"))));
                }
            }
            finally
            {
                result.Close();
            }
            return routesNotNotified;
        }


        // Unneeded functionality for when a cancled link could be replace.
        //public TradeLink ReplaceTradeLink(TradeLink originalLink, BookHave have, BookWant want)
        //{
        //    TradeLink newLink = TradeLink.CreateNewTradeLink(this, have, want);
        //    SqlConnection connection = DatabaseConnectionManager.Current.Connection;
        //    SqlCommand query = new SqlCommand("EXEC replace_link_in_route @routeid, @original_linkid, @new_linkid", connection);
        //    query.Parameters.Add("@routeid", System.Data.SqlDbType.UniqueIdentifier);
        //    query.Parameters["@routeid"].SqlValue = this.ID;
        //    query.Parameters.Add("@original_linkid", System.Data.SqlDbType.UniqueIdentifier);
        //    query.Parameters["@original_linkid"].SqlValue = originalLink.ID;
        //    query.Parameters.Add("@new_linkid", System.Data.SqlDbType.UniqueIdentifier);
        //    query.Parameters["@new_linkid"].SqlValue = newLink.ID;
        //    query.ExecuteNonQuery();
        //    //*****LinkedListNode<TradeLink> previousLink = TradeLinks.Find(originalLink).Previous;
        //    //TradeLinks.Remove(originalLink);
        //    //if (previousLink == null)
        //    //    TradeLinks.AddFirst(newLink);
        //    //else
        //    //    TradeLinks.AddAfter(previousLink, newLink);
        //    return newLink;
        //}

    }
}
